grammar Lua2;

options 
{
    language=CSharp2; 
 	//ASTLabelType=CommonTree;
    output=AST;
    backtrack = true;
    memoize = true;
}

tokens 
{
    	BLOCK;
}

@lexer::namespace		{PSLib.Parsing.Lua}
@lexer::header 
{
	using System.Collections.Generic;
	using System.Text;
}

@lexer::members {
    public bool noCloseAhead(int numEqSigns) {
        if(input.LA(1) != ']') return true;
        for(int i = 2; i < numEqSigns+2; i++) {
            if(input.LA(i) != '=') return true;
        }
        return input.LA(numEqSigns+2) != ']';
    }

    public void matchClose(int numEqSigns){
        StringBuilder eqSigns = new StringBuilder();
        for(int i = 0; i < numEqSigns; i++) {
            eqSigns.Append('=');
        }
        Match("]"+eqSigns+"]");
    }
}

@parser::namespace		{PSLib.Parsing.Lua}
@parser::header 
{
	using System.Collections.Generic;
	using PSLib.Parsing.Common;
}

@parser::members 
{
	
}

program	@init{int startPos = this.Input.Index(); }: (chunk | EOF) -> ^(BLOCK["program",this.Input,startPos,this.Input.Index()-1] chunk);
chunk : (stat (';')?)* (laststat (';')?)? ;

block : ( chunk? ) ;

stat  : var_assignment
		| functioncall
		| do_block
		| while_stat
		| repeat_stat
		| if_stat
		| for_stat
		| func_definition
		| var_definition ;
		
var_definition:	'local' namelist ('=' explist1)? ;

var_assignment	: varlist1 '='! explist1;

func_definition : 'local' 'function' NAME func_body
				| 'function' funcname func_body ;

for_stat 
	: 'for' NAME '=' exp ',' exp (',' exp)? 'do' block 'end' 
	| 'for' namelist 'in' explist1 'do' block 'end' ;
				
if_stat  :'if' exp 'then' block ('elseif' exp 'then' block )* ('else' block)? 'end';

laststat : 'return' (explist1)? | 'break';

repeat_stat : 'repeat' block 'until' exp;

while_stat	: 'while' exp 'do' block 'end';

do_block	@init{int startPos = this.Input.Index(); }: 'do' block 'end' -> ^(BLOCK["do_block",this.Input,startPos,this.Input.Index()-1] block);

varlist1 	: var (',' var )*;

funcname	: NAME ('.' NAME)* (':' NAME)? ;

namelist 	: NAME ( ',' NAME)* ;

explist1	: (exp ',')* exp ;

exp :	(
			( 'nil'
			| 'false'
			| 'true'
			| number
			| stringToken
			| '...'
			| function
			| prefixexp
			| tableconstructor
			| unop exp			) (binop exp)* );
	 
binop : ('+' | '-' | '*' | '/' | '^' | '%' | '..' | '<' | '<=' | '>' | '>=' | '==' | '~=' | 'and' | 'or');

var : ( NAME | '(' exp ')' varSuffix ) ( varSuffix )* ;

prefixexp : varOrExp (nameAndArgs)* ;

functioncall: varOrExp nameAndArgs+;

varOrExp : var | '(' exp ')' ;

nameAndArgs : (':' NAME )? args ; 

varSuffix : (nameAndArgs)* ( '[' exp ']' | ('.' NAME )) ;

args :  '(' (explist1)? ')' | tableconstructor | stringToken ;

function : 'function' func_body ;

func_body : '(' (func_parlist )? ')' block 'end' ;

func_parlist :(namelist (',' '...' )? | '...' ) ;

tableconstructor : '{' (fieldlist)? '}' ;

fieldlist : field (fieldsep field )* (fieldsep)?;

field : '[' exp ']' '=' exp | NAME '=' exp | exp ;

fieldsep : ',' | ';';

unop : '-' | 'not' | '#';

number : INT | FLOAT | EXP | HEX;

stringToken	: NORMALSTRING
			| CHARSTRING
			| LONGSTRING
			;
//Tokens

NAME  : ('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'0'..'9'|'_')*  ;


LINE_COMMENT:   (
				  '--' ~('\n'|'\r')* (LINE_BREAK | EOF) 
				| ('#!' ~('\n'|'\r')* (LINE_BREAK | EOF))
				) {$channel=2;};
COMMENT:'--' LONGSTRING {$channel=2;};

WS  :   ( ' ' | '\t' | '\r' | '\n' )+ {$channel=3;};

INT	: ('0'..'9')+;
FLOAT 	: INT? '.' INT ;

EXP	: (INT| FLOAT) ('E'|'e') ('-')? INT;

HEX	:'0x' ('0'..'9'| 'a'..'f' | 'A'..'F')+ ;

//String tokens
NORMALSTRING	: '"'  ( ~('"' |'\\'|'\n'|'\r') | '\\'.)* '"' ;
CHARSTRING		: '\'' ( ~('\''|'\\'|'\n'|'\r') | '\\'.)* '\'' ;
//Operator tokens
	
//Keyword tokens

//Chars
LONGSTRING
@init {int openEq = 0;}
  :  '[' ('=' {openEq++;})* '[' ({noCloseAhead(openEq)}?=> .)* {matchClose(openEq);}
  ;
  
fragment LINE_BREAK: '\r'? '\n';

fragment
HEX_DIGIT : ('0'..'9'|'a'..'f'|'A'..'F') ;

fragment
ESC_SEQ
    :   '\\' ('b'|'t'|'n'|'f'|'r'|'\"'|'\''|'\\')
    |   UNICODE_ESC
    |   OCTAL_ESC
    ;

fragment
OCTAL_ESC
    :   '\\' ('0'..'3') ('0'..'7') ('0'..'7')
    |   '\\' ('0'..'7') ('0'..'7')
    |   '\\' ('0'..'7')
    ;

fragment
UNICODE_ESC: '\\' 'u' HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT;

